package com.homedo.microservice.open.service.gateway.manager.web.gateway.service;

import com.google.common.collect.Lists;
import com.homedo.common.bean.data.enums.ResponseEnum;
import com.homedo.common.bean.vo.Pagination;
import com.homedo.common.bean.vo.context.UserContext;
import com.homedo.common.dao.bean.po.base.BasePo;
import com.homedo.common.dao.bean.po.enhanced.base.BaseEnhanced;
import com.homedo.common.exception.HomedoException;
import com.homedo.common.service.BaseService;
import com.homedo.common.util.Detect;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.bean.enums.forroute.StatusEnum4Route;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.bean.enums.forroutedomain.StatusEnum4RouteDomain;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.bean.enums.forrouteservice.StatusEnum4RouteService;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.bean.po.Route;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.bean.po.RouteVersion;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.bean.po.enhanced.RouteDomainEnhanced;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.bean.po.enhanced.RouteEnhanced;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.bean.po.enhanced.RouteServiceEnhanced;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.bean.po.enhanced.RouteVersionEnhanced;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.bean.query.RouteDomainQuery;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.bean.query.RouteQuery;
import com.homedo.microservice.open.service.gateway.manager.web.gateway.persistence.dao.RouteDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 作者 jgshun
 * @date 创建时间 2018年09月18日 11时30分20秒
 */
@Service
public class RouteService extends BaseService<Route> {
    @Autowired
    private RouteDao routeDao;
    @Autowired
    private RouteDomainService routeDomainService;
    @Autowired
    private RouteServiceService routeServiceService;
    @Autowired
    private RouteVersionService routeVersionService;


    @Override
    public List<RouteEnhanced> getEnhanceds(List<?> ids, UserContext userContext) {
        List<Route> routes = routeDao.getListByIds(ids, Route.class);

        if (!Detect.notEmpty(routes)) {
            return null;
        }

        List<RouteEnhanced> routeEnhanceds = this.convent2Enhanceds(routes);

        this.appendEnhancedCommons(routeEnhanceds, userContext);

        return routeEnhanceds;
    }


    @Override
    public List<RouteEnhanced> getEnhanceds4Active(List<?> ids, UserContext userContext) {
        List<Route> routes = routeDao.getListByIdsGreaterThanMark0(ids, Route.class);

        if (!Detect.notEmpty(routes)) {
            return null;
        }
        List<RouteEnhanced> routeEnhanceds = this.convent2Enhanceds(routes);

        this.appendEnhancedCommons(routeEnhanceds, userContext);

        return routeEnhanceds;
    }


    @Override
    public List<RouteEnhanced> convent2Enhanceds(List<? extends BasePo> pos) {
        List<Route> routes = (List<Route>) pos;
        List<RouteEnhanced> routeEnhanceds = Lists.newArrayList();
        routes.forEach(e -> routeEnhanceds.add(new RouteEnhanced(e)));
        return routeEnhanceds;
    }


    public List<RouteEnhanced> getEnhanceds4Application(String applicationName, List<StatusEnum4Route> statuses, UserContext userContext) {
        List<Route> routes = routeDao.getByApplication(applicationName, statuses);

        if (!Detect.notEmpty(routes)) {
            return null;
        }
        List<RouteEnhanced> routeEnhanceds = this.convent2Enhanceds(routes);

        this.appendEnhancedCommons(routeEnhanceds, userContext);

        return routeEnhanceds;
    }

    public Pagination<RouteEnhanced> getPagination(RouteQuery routeQuery, Pagination<RouteEnhanced> pagination, UserContext userContext) {
        if (Detect.notEmpty(routeQuery.getLikeDomain())) {
            RouteDomainQuery routeDomainQuery = new RouteDomainQuery();
            routeDomainQuery.setLikeDomain(routeQuery.getLikeDomain());
            routeDomainQuery.setApplicationNames(routeQuery.getApplicationNames());

            Pagination<RouteDomainEnhanced> routeDomainEnhancedPagination = new Pagination<>(1, Integer.MAX_VALUE, false);
            routeDomainEnhancedPagination = routeDomainService.getPagination(routeDomainQuery, routeDomainEnhancedPagination, userContext);

            if (!Detect.notEmpty(routeDomainEnhancedPagination.getItems())) {
                return pagination;
            }
            List<Integer> domainRouteIds = routeDomainEnhancedPagination.getItems().stream().map(RouteDomainEnhanced::getId).collect(Collectors.toList());
            routeQuery.setDomainRouteIds(domainRouteIds);
        }


        Pagination<Route> routePagination = routeDao.getPagination(routeQuery, pagination.copy());

        pagination.setCount(routePagination.getCount());

        if (!Detect.notEmpty(routePagination.getItems())) {
            return pagination;
        }

        pagination.setItems(this.convent2Enhanceds(routePagination.getItems()));
        this.appendEnhancedCommons(pagination.getItems(), userContext);

        return pagination;
    }

    @Override
    public List<RouteEnhanced> appendEnhancedCommons(List<? extends BaseEnhanced> enhancedItems, UserContext userContext) {
        List<RouteEnhanced> routeEnhanceds = (List<RouteEnhanced>) enhancedItems;

        this.appendRouteServiceEnhanced(routeEnhanceds, userContext);
        this.appendRouteDomainEnhanced(routeEnhanceds, userContext);
        this.appendRouteVersionEnhanceds(routeEnhanceds, userContext);

        return routeEnhanceds;
    }

    public List<RouteEnhanced> appendRouteVersionEnhanceds(List<RouteEnhanced> routeEnhanceds, UserContext userContext) {

        List<String> routeIds = routeEnhanceds.stream().map(RouteEnhanced::getId).collect(Collectors.toList());

        List<RouteVersionEnhanced> routeVersionEnhanceds = routeVersionService.getEnhanceds4Route(routeIds, userContext);

        if (!Detect.notEmpty(routeVersionEnhanceds)) {
            return routeEnhanceds;
        }

        this.appendRouteVersionEnhanceds(routeEnhanceds, routeVersionEnhanceds, userContext);

        return routeEnhanceds;

    }

    public List<RouteEnhanced> appendRouteDomainEnhanced(List<RouteEnhanced> routeEnhanceds, UserContext userContext) {
        List<Integer> routeDomainIds = routeEnhanceds.stream()
                .map(RouteEnhanced::getRouteDomainEnhanced).filter(e -> e != null && e.getId() != null && e.getId() > 0).collect(Collectors.toList())
                .stream().map(RouteDomainEnhanced::getId).collect(Collectors.toList());
        if (!Detect.notEmpty(routeDomainIds)) {
            return routeEnhanceds;
        }
        List<RouteDomainEnhanced> routeDomainEnhanceds = routeDomainService.getEnhanceds(routeDomainIds, userContext);

        if (!Detect.notEmpty(routeDomainEnhanceds)) {
            return routeEnhanceds;
        }

        this.appendRouteDomainEnhanced(routeEnhanceds, routeDomainEnhanceds, userContext);

        return routeEnhanceds;
    }

    public List<RouteEnhanced> appendRouteServiceEnhanced(List<RouteEnhanced> routeEnhanceds, UserContext userContext) {
        List<String> routeServiceIds = routeEnhanceds.stream()
                .map(RouteEnhanced::getRouteServiceEnhanced).filter(e -> e != null && Detect.notEmpty(e.getId())).collect(Collectors.toList())
                .stream().map(RouteServiceEnhanced::getId).collect(Collectors.toList());
        if (!Detect.notEmpty(routeServiceIds)) {
            return routeEnhanceds;
        }


        List<RouteServiceEnhanced> routeServiceEnhanceds = routeServiceService.getEnhanceds(routeServiceIds, userContext);

        if (!Detect.notEmpty(routeServiceEnhanceds)) {
            return routeEnhanceds;
        }

        this.appendRouteServiceEnhanced(routeEnhanceds, routeServiceEnhanceds, userContext);

        return routeEnhanceds;
    }

    private void appendRouteServiceEnhanced(List<RouteEnhanced> routeEnhanceds, List<RouteServiceEnhanced> routeServiceEnhanceds, UserContext userContext) {
        routeEnhanceds.forEach(e -> {
            this.appendRouteServiceEnhanced(e, routeServiceEnhanceds, userContext);
        });
    }

    private void appendRouteServiceEnhanced(RouteEnhanced routeEnhanced, List<RouteServiceEnhanced> routeServiceEnhanceds, UserContext userContext) {
        routeServiceEnhanceds.forEach(e -> {
            if (routeEnhanced.getRouteServiceEnhanced() != null && e.getId().equals(routeEnhanced.getRouteServiceEnhanced().getId())) {
                routeEnhanced.setRouteServiceEnhanced(e);
                return;
            }
        });
    }


    private void appendRouteDomainEnhanced(List<RouteEnhanced> routeEnhanceds, List<RouteDomainEnhanced> routeDomainEnhanceds, UserContext userContext) {
        routeEnhanceds.forEach(e -> {
            this.appendRouteDomainEnhanced(e, routeDomainEnhanceds, userContext);
        });
    }

    private void appendRouteDomainEnhanced(RouteEnhanced routeEnhanced, List<RouteDomainEnhanced> routeDomainEnhanceds, UserContext userContext) {
        routeDomainEnhanceds.forEach(e -> {
            if (routeEnhanced.getRouteDomainEnhanced() != null && e.getId() == routeEnhanced.getRouteDomainEnhanced().getId().intValue()) {
                routeEnhanced.setRouteDomainEnhanced(e);
                return;
            }
        });
    }

    private void appendRouteVersionEnhanceds(List<RouteEnhanced> routeEnhanceds, List<RouteVersionEnhanced> routeVersionEnhanceds, UserContext userContext) {
        routeEnhanceds.forEach(e -> {
            this.appendRouteVersionEnhanceds(e, routeVersionEnhanceds, userContext);
        });
    }

    private void appendRouteVersionEnhanceds(RouteEnhanced routeEnhanced, List<RouteVersionEnhanced> routeVersionEnhanceds, UserContext userContext) {
        List<RouteVersionEnhanced> curRouteVersionEnhanced = Lists.newArrayList();
        routeVersionEnhanceds.forEach(e -> {
            if (e.getRouteEnhanced().getId().equals(routeEnhanced.getId())) {
                curRouteVersionEnhanced.add(e);
            }
        });
        routeEnhanced.setRouteVersionEnhanceds(curRouteVersionEnhanced);
    }

    public Route addRoute(Route route, UserContext userContext) {
        this.checkAdd(route);

        route.setCustomSensitiveHeaders(true);
        route.setRetryable(false);
        route.setCreateTime(new Date());
        route.setUpdateTime(new Date());

        route = routeDao.saveSelective(route);

        return route;
    }

    public void updateRoute(String id, String path, String serviceId, Boolean stripPrefix, String addPrefix,
                            String sensitiveHeaders, Boolean addHostHeader, Integer status,
                            Double sort, Integer domainRouteId, String comment, UserContext userContext) {
        Route route = routeDao.getById(id);

        this.checkUpdate(route, path, serviceId, domainRouteId);

        if (Detect.notEmpty(path)) {
            route.setPath(path);
        }

        route.setAddPrefix(addPrefix == null ? "" : addPrefix);

        if (Detect.notEmpty(serviceId)) {
            route.setServiceId(serviceId);
        }
        if (stripPrefix != null) {
            route.setStripPrefix(stripPrefix);
        }

        if (sensitiveHeaders != null) {
            route.setSensitiveHeaders(sensitiveHeaders);
        }

        if (addHostHeader != null) {
            route.setAddHostHeader(addHostHeader);
        }
        if (status != null) {
            route.setStatus(status);
        }
        if (sort != null) {
            route.setSort(sort);
        }
        if (domainRouteId != null) {
            route.setDomainRouteId(domainRouteId);
        }
        if (Detect.notEmpty(comment)) {
            route.setComment(comment);
        }

        route.setUpdateTime(new Date());

        routeDao.updateByPrimaryKeySelective(route);
    }

    private void checkUpdate(Route dbRoute, String path, String serviceId, Integer domainRouteId) {
        if (Detect.notEmpty(path) && !path.startsWith("/")) {
            throw new HomedoException("[路径必须以/开头]");
        }
        if (Detect.notEmpty(serviceId) && !serviceId.equals(dbRoute.getServiceId())) {
            RouteServiceEnhanced routeServiceEnhanced = routeServiceService.getEnhanced(serviceId, null);
            if (routeServiceEnhanced.getStatus() != StatusEnum4RouteService.EFFECTIVE) {
                throw new HomedoException("[组不可用]");
            }

            if (!routeServiceEnhanced.getApplicationName().equals(dbRoute.getApplicationName())) {
                throw new HomedoException(ResponseEnum.PERMISSION_DENIED);
            }
        }

        if (domainRouteId != null && domainRouteId > 0 && !domainRouteId.equals(dbRoute.getDomainRouteId())) {
            RouteDomainEnhanced routeDomainEnhanced = routeDomainService.getEnhanced(domainRouteId, null);
            if (routeDomainEnhanced.getStatus() != StatusEnum4RouteDomain.EFFECTIVE) {
                throw new HomedoException("[域名不可用]");
            }

            if (!routeDomainEnhanced.getApplicationName().equals(dbRoute.getApplicationName())) {
                throw new HomedoException(ResponseEnum.PERMISSION_DENIED);
            }
        }
    }


    private void checkAdd(Route route) {
        if (!Detect.notEmpty(route.getId()) || !Detect.notEmpty(route.getApplicationName())
                || !Detect.notEmpty(route.getPath()) || !Detect.notEmpty(route.getServiceId())
                || !Detect.notEmpty(route.getComment())
                || route.getStripPrefix() == null || route.getStatus() == null
                || route.getAddHostHeader() == null) {
            throw new HomedoException(ResponseEnum.PARAM_ERROR);
        }
        if (!route.getPath().startsWith("/")) {
            throw new HomedoException("[路径必须以/开头]");
        }

        RouteServiceEnhanced routeServiceEnhanced = routeServiceService.getEnhanced(route.getServiceId(), null);
        if (routeServiceEnhanced.getStatus() != StatusEnum4RouteService.EFFECTIVE) {
            throw new HomedoException("[组不可用]");
        }

        if (!routeServiceEnhanced.getApplicationName().equals(route.getApplicationName())) {
            throw new HomedoException(ResponseEnum.PERMISSION_DENIED);
        }

        if (route.getDomainRouteId() != null && route.getDomainRouteId() > 0) {
            RouteDomainEnhanced routeDomainEnhanced = routeDomainService.getEnhanced(route.getDomainRouteId(), null);
            if (routeDomainEnhanced.getStatus() != StatusEnum4RouteDomain.EFFECTIVE) {
                throw new HomedoException("[域名不可用]");
            }

            if (!routeDomainEnhanced.getApplicationName().equals(route.getApplicationName())) {
                throw new HomedoException(ResponseEnum.PERMISSION_DENIED);
            }
        }

        Route record = new Route();
        if (route.getDomainRouteId() != null && route.getDomainRouteId() > 0) {
            record.setDomainRouteId(route.getDomainRouteId());
        } else {
            record.setDomainRouteId(0);
        }
        record.setPath(route.getPath());
        record.setApplicationName(route.getApplicationName());
        record = routeDao.getOneByRecord(record);

        if (record != null) {
            throw new HomedoException("[同一域名下路径不可重复]");
        }
    }

    public void deleteRoute(String id, UserContext userContext) {
        RouteVersion routeVersionRecord = new RouteVersion();
        routeVersionRecord.setRouteId(id);
        routeVersionService.delete(routeVersionRecord, userContext);//删除关联的版本路由

        routeDao.deleteById(id);//删除路由
    }
}
